#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "share.h"

void ZqHexDump(const char *head, unsigned char* data, unsigned int len) 
{
	char hex_tb[16] = "0123456789ABCDEF";
	int x = len / 16;
	char buf[54];
	ZQLOG("%s", head);
	for (int i = 0; i < x; i++) {
		char* p = buf;
		for (int j = 0; j < 16; j++, data++) {
			if (j == 8) { *(p++) = '-'; *(p++) = ' '; }
			*(p++) = hex_tb[(*data >> 4) & 0xf];
			*(p++) = hex_tb[*data & 0xf];
			*(p++) = ' ';
		}
		*p = '\0';
		ZQLOG("%08x->%s", (size_t)(data - 16), buf);
	}
	x = len % 16;
	if (x == 0) return;
	char* p = buf;
	for (int j = 0; j < x; j++, data++) {
		if (j == 8) { *(p++) = '-'; *(p++) = ' '; }
		*(p++) = hex_tb[(*data >> 4) & 0xf];
		*(p++) = hex_tb[*data & 0xf];
		*(p++) = ' ';
	}
	*p = '\0';
	ZQLOG("%08x->%s", (size_t)(data - x), buf);
}

// xor checksum
//__HIGH_CODE
//uint8_t ZqGetChecksum(const uint8_t* data, unsigned int len)
//{
//    uint8_t s = 0;
//    uint8_t* end = (uint8_t*)data + len;
//    while (data < end)
//        s ^= *(data++);
//    return s;
//}
// xor checksum, fast version
__HIGH_CODE
uint8_t ZqGetChecksum(uint8_t* data, unsigned int len)
{
	uint8_t s8 = 0;
	if (len <= 16) { // 数据少，不值得折腾
		uint8_t* end = data + len;
		while (data < end) s8 ^= *(data++);
		return s8;
	}
	// make data start with four-byte aligned ***
	int rem = ((size_t)data) % 4;
	switch (rem) {
	case 1:
		s8 ^= *(data++);
		len--;
	case 2:
		s8 ^= *(data++);
		len--;
	case 3:
		s8 ^= *(data++);
		len--;
	}
	uint32_t s32 = 0;
	rem = len % 4;
	// Fast XOR check in a four-byte manner
	for (uint8_t* end = data + (len - rem); data < end; data += 4)
		s32 ^= *((uint32_t*)data);
	// Merge 4 bytes into a single byte
	s8 = s8 ^ ((uint8_t)s32) ^ ((uint8_t)(s32 >> 8)) ^ ((uint8_t)(s32 >> 16)) ^ ((uint8_t)(s32 >> 24));
	// deal with remainder
	switch (rem) {
	case 3:
		s8 ^= *(data++);
	case 2:
		s8 ^= *(data++);
	case 1:
		s8 ^= *(data++);
	}
	return s8;
}

// other functions -----------------------------------------------------------------------------------------------
uint8_t GetVbatPercentage(short vbat)
{
	if(IsWordValid(vbat) == false) return 0;
    if (vbat > 4100) return 100;
    else if (vbat > 4000) return 95 + (vbat - 4000) * (100 - 95) / 100;
    else if (vbat > 3900) return 85 + (vbat - 3900) * (95 - 85) / 100;
    else if (vbat > 3800) return 75 + (vbat - 3800) * (85 - 75) / 100;
    else if (vbat > 3700) return 65 + (vbat - 3700) * (75 - 65) / 100;
    else if (vbat > 3600) return 50 + (vbat - 3600) * (65 - 50) / 100;
    else if (vbat > 3500) return 25 + (vbat - 3500) * (50 - 25) / 100;
    else if (vbat > 3400) return 0 + (vbat - 3400) * (25 - 0) / 100;
    else return 0;
}

// create the linear buffer, max len < 32k
ZqFifoBuf *ZqFifoBufCreate(uint16_t size, void (*lock)(uint8_t locked))
{
    if(size >= 0x8000) return NULL; // max len < 32k
    ZqFifoBuf *buf = (ZqFifoBuf*)malloc(sizeof(ZqFifoBuf) + size);
    if (buf == NULL) return NULL;
    buf->max_size = size;
    buf->lock = lock;
    buf->lock = lock;
    return buf;
}
// flush the len-length data from the 0-offset, and copy the left data to the begin of the buffer, return the left data len
uint16_t ZqFifoBufFlush(ZqFifoBuf *buf, uint16_t len)
{
	if(len == 0) return buf->len;
    if (buf->lock != NULL) buf->lock(1);
    int left = (int)buf->len - (int)len;
    if(left > 0) {
        memmove(buf->data, buf->data + len, left);
        buf->len = left;
    }
    else 
        buf->len = 0;
    if (buf->lock != NULL) buf->lock(0);
    return buf->len;
}
// write data to buf
uint16_t ZqFifoBufWrite(ZqFifoBuf *buf, uint8_t *data, uint16_t len)
{
    if (buf->lock != NULL) buf->lock(1);
    uint16_t left = buf->max_size - buf->len;
    if (left >= len) {
        memcpy(buf->data + buf->len, data, len);
        buf->len += len;
    }
    if (buf->lock != NULL) buf->lock(0);
    return (left >= len)? len : 0;
}



// covert date to second from 1970, 参考网上unix标准算法.
uint32_t ZqDateToTimestamp(uint16_t year, uint16_t month, uint16_t day, uint16_t hour, uint16_t min, uint16_t sec)
{
	int mon = (int)month;
    unsigned int ret;
    // 为了后续算法，调整每年月从3月开始
    if (0 >= (int)(mon -= 2)) {    
        mon += 12;    
        year -= 1;
    }
    // days
    ret = (unsigned int)(year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) + year * 365 - 719499;
    // hours
    ret = (ret * 24) + hour;
    // second
    return (ret * 60 + min) * 60 + sec;
}
// covert second from 1970 to date, 参考网上unix标准算法. note: begin from 2024-01-01 00:00:00
void ZqTimestampToDate(uint32_t timestamp, uint16_t *year, uint16_t *mon, uint16_t *day, uint16_t *hour, uint16_t *min, uint16_t *sec) 
{
    const int days_in_mon[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int is_leap_year;
    uint32_t sec_in_day = 86400;
    uint32_t sec_in_hour = 3600;
    uint32_t sec_in_minute = 60;

    *year = 2024;
    *mon = 1;
	if(timestamp < 1704067200) {// 2024-01-01 00:00:00
		*day = 1;
		*hour = *min = *sec = 0;
		return;
	}
	timestamp -= 1704067200;
    // 计算年份
    while (timestamp >= (is_leap_year = ((*year % 4 == 0 && *year % 100 != 0) || (*year % 400 == 0)) ? 366 : 365) * sec_in_day) {
        timestamp -= is_leap_year * sec_in_day;
        (*year)++;
    }
    // 计算月份
    while (timestamp >= days_in_mon[*mon - 1] * sec_in_day) {
        if (*mon == 2 && is_leap_year) {
            timestamp -= 29 * sec_in_day;
        } else {
            timestamp -= days_in_mon[*mon - 1] * sec_in_day;
        }
        (*mon)++;
    }
    // 计算日期
    *day = timestamp / sec_in_day + 1;
    timestamp %= sec_in_day;
    // 计算小时
    *hour = timestamp / sec_in_hour;
    timestamp %= sec_in_hour;
    // 计算分钟
    *min = timestamp / sec_in_minute;
    timestamp %= sec_in_minute;
    // 计算秒钟
    *sec = timestamp;
}


//// general circle buffer fuctions -----------------------------------------------------------------------------------------------
//// default lock function
//static void ZqCircleBufLock_def(uint8_t locked)
//{
//    return;
//}
//// create the linear buffer, max len < 32k
//ZqCircleBuf* ZqCircleBufCreate(uint16_t size, void (*lock)(uint8_t locked))
//{
//    if (size >= 0x8000) return NULL; // max len < 32k
//    ZqCircleBuf* c_buf = (ZqCircleBuf*)malloc(sizeof(ZqCircleBuf) + size);
//    if (c_buf == NULL) return c_buf;
//    c_buf->buf_size = size;
//    c_buf->r_pos = c_buf->data_len = c_buf->overflow = 0;
//    c_buf->lock = (lock) ? lock : ZqCircleBufLock_def;
//    return c_buf;
//}
//uint8_t ZqCircleBufIsOverflow(ZqCircleBuf* c_buf)
//{
//    return c_buf->overflow;
//}
//uint16_t ZqCircleBufGetDataLen(ZqCircleBuf* c_buf)
//{
//    return c_buf->data_len;
//}
//// discard len of data, return left data length
//uint16_t ZqCircleBufDiscard(ZqCircleBuf* c_buf, uint16_t len)
//{
//    c_buf->lock(1);
//    {
//        if (len >= c_buf->data_len) // clear buffer
//            c_buf->r_pos = c_buf->data_len = 0;
//        else {
//            c_buf->data_len -= len;
//            c_buf->r_pos = (c_buf->r_pos + len) % c_buf->buf_size;
//        }
//        c_buf->overflow = 0;
//    }
//    c_buf->lock(0);
//    return c_buf->data_len;
//}
//// directly get data buf and data len in the c_buf stream, use can use the data without copy out, once using done, call discard fucntion to realese data
//uint8_t* ZqCircleBufGetBuf(ZqCircleBuf* c_buf, uint16_t* plen)
//{
//    *plen = c_buf->data_len;
//
//    c_buf->lock(1); // 上锁
//    short loop = (c_buf->r_pos + *plen) - c_buf->buf_size;
//    if (loop > 0) {  // read buffer is loop, Organize the buffers and start the data from begining of the buffer
//        memcpy(c_buf->buf + *plen - loop, c_buf->buf, loop);
//        memcpy(c_buf->buf, c_buf->buf + c_buf->r_pos, *plen - loop);
//        c_buf->r_pos = 0;
//    }
//    c_buf->lock(0);
//    return (c_buf->buf + c_buf->r_pos);
//}
//uint16_t ZqCircleBufRead(ZqCircleBuf* c_buf, uint8_t* buf, uint16_t len)
//{
//
//    uint16_t data_len;
//    if (len > c_buf->data_len) len = c_buf->data_len;
//    short loop = (c_buf->r_pos + len) - c_buf->buf_size;
//    if (loop < 0)
//        memcpy(buf, c_buf->buf + c_buf->r_pos, len);
//    else {
//        memcpy(buf, c_buf->buf + c_buf->r_pos, c_buf->buf_size - loop);
//        memcpy(buf + c_buf->buf_size - loop, c_buf->buf, loop);
//    }
//    ZqCircleBufDiscard(c_buf, len);
//    return len;
//}
//uint16_t ZqCircleBufWrite(ZqCircleBuf* c_buf, uint8_t* data, uint16_t len)
//{
//    if (c_buf->overflow == 1) return 0; 			// in the past has overflowed, no more write in
//
//    c_buf->lock(1);
//    if ((c_buf->buf_size - c_buf->data_len) >= len) {
//        uint16_t w_pos = (c_buf->r_pos + c_buf->data_len) % c_buf->buf_size;
//        short loop = w_pos + len - c_buf->buf_size;
//        if (loop > 0) {
//            memcpy(c_buf->buf + w_pos, data, c_buf->buf_size - w_pos);
//            memcpy(c_buf->buf, data + c_buf->buf_size - w_pos, loop);
//        }
//        else {
//            memcpy(c_buf->buf + w_pos, data, len);
//        }
//        c_buf->data_len += len;
//    }
//    else {  // overflow
//        c_buf->overflow = 1;
//        len = 0;
//    }
//    c_buf->lock(0);
//    return len;
//}
